/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2017 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.axelor.apps.base.service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.axelor.apps.base.db.IProductVariant;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.ProductVariant;
import com.axelor.apps.base.db.ProductVariantAttr;
import com.axelor.apps.base.db.ProductVariantConfig;
import com.axelor.apps.base.db.ProductVariantValue;
import com.axelor.apps.base.db.SupplierCatalog;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.ProductVariantRepository;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.exception.AxelorException;
import com.beust.jcommander.internal.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;

public class ProductServiceImpl implements ProductService  {

	@Inject
	private ProductVariantService productVariantService;
	
	@Inject
	private ProductVariantRepository productVariantRepo;

	@Inject
	protected GeneralService generalService;
	
	@Inject
	private ProductRepository productRepo;

	@Override
	@Transactional(rollbackOn = {AxelorException.class, Exception.class})
	public void updateProductPrice(Product product)  {

		this.updateSalePrice(product);

		productRepo.save(product);

	}


	/**
	 * Retourne le prix d'un produit à une date t.
	 *
	 * @param product
	 * @param date
	 * @return
	 */
	@Override
	public BigDecimal getPrice(Product product, boolean isPurchase){

		if(isPurchase)  {  return product.getPurchasePrice();  }
		else  {  return product.getSalePrice();  }

	}


	@Override
	public void updateSalePrice(Product product)  {

		BigDecimal managePriceCoef = product.getManagPriceCoef();

		if(product.getCostPrice() != null)  {

			if(product.getProductVariant() != null)  {

				product.setCostPrice(product.getCostPrice().add(this.getProductExtraPrice(product.getProductVariant(), IProductVariant.APPLICATION_COST_PRICE)));

			}

		}

		if(product.getCostPrice() != null && managePriceCoef != null)  {

			product.setSalePrice((product.getCostPrice().multiply(managePriceCoef)).setScale(generalService.getNbDecimalDigitForUnitPrice(), BigDecimal.ROUND_HALF_UP));

			if(product.getProductVariant() != null)  {

				product.setSalePrice(product.getSalePrice().add(this.getProductExtraPrice(product.getProductVariant(), IProductVariant.APPLICATION_SALE_PRICE)));

			}
		}

		if(product.getProductVariantConfig() != null)  {

			this.updateSalePriceOfVariant(product);

		}
	}


	private void updateSalePriceOfVariant(Product product)  {

		List<? extends Product> productVariantList = productRepo.all().filter("self.parentProduct = ?1", product).fetch();

		for(Product productVariant : productVariantList)  {

			productVariant.setCostPrice(product.getCostPrice());
			productVariant.setSalePrice(product.getSalePrice());
			productVariant.setManagPriceCoef(product.getManagPriceCoef());

			this.updateSalePrice(productVariant);

		}


	}



	@Override
	@Transactional(rollbackOn = {AxelorException.class, Exception.class})
	public void generateProductVariants(Product productModel)  {

		List<ProductVariant> productVariantList = this.getProductVariantList(productModel.getProductVariantConfig());

		int seq = 1;
		
		for(ProductVariant productVariant : productVariantList)  {

			productVariantRepo.save(productVariant);

			productRepo.save(this.createProduct(productModel, productVariant, seq++));

		}

	}


	@Override
	public Product createProduct(Product productModel, ProductVariant productVariant, int seq)  {

		String description = "";
		String internalDescription = "";
		
		if(productModel.getDescription() != null)  {
			description = productModel.getDescription();
		}
		if(productModel.getInternalDescription() != null)  {
			internalDescription = productModel.getInternalDescription();
		}
		description += "<br>"+productVariant.getName();
		internalDescription += "<br>"+productVariant.getName();

		Product product = new Product(
				productModel.getName()+" ("+productVariant.getName()+")",
				productModel.getCode()+"-"+seq,
				description,
				internalDescription,
				productModel.getPicture(),
				productModel.getProductCategory(),
				productModel.getProductFamily(),
				productModel.getUnit(),
				productModel.getSaleSupplySelect(),
				productModel.getProductTypeSelect(),
				productModel.getProcurementMethodSelect(),
				productModel.getIsRawMaterial(),
				productModel.getSaleCurrency(),
				productModel.getPurchaseCurrency(),
				productModel.getStartDate(),
				productModel.getEndDate());

		productModel.setIsModel(true);

		product.setIsModel(false);
		product.setParentProduct(productModel);
		product.setProductVariant(productVariant);
		
		product.setCostPrice(productModel.getCostPrice());
		product.setSalePrice(productModel.getSalePrice());
		product.setManagPriceCoef(productModel.getManagPriceCoef());

		this.updateSalePrice(product);

		return product;
	}


	/**
	 *
	 * @param productVariant
	 * @param applicationPriceSelect
	 * 		- 1 : Sale price
	 * 		- 2 : Cost price
	 * @return
	 */
	@Override
	public BigDecimal getProductExtraPrice(ProductVariant productVariant, int applicationPriceSelect)  {

		BigDecimal extraPrice = BigDecimal.ZERO;

		ProductVariantValue productVariantValue1 = productVariant.getProductVariantValue1();
		ProductVariantValue productVariantValue2 = productVariant.getProductVariantValue2();
		ProductVariantValue productVariantValue3 = productVariant.getProductVariantValue3();
		ProductVariantValue productVariantValue4 = productVariant.getProductVariantValue4();


		if(productVariantValue1 != null && productVariantValue1.getApplicationPriceSelect() == applicationPriceSelect)  {

			extraPrice = extraPrice.add(productVariantValue1.getPriceExtra());

		}

		if(productVariantValue2 != null)  {

			extraPrice = extraPrice.add(productVariantValue2.getPriceExtra());

		}

		if(productVariantValue3 != null)  {

			extraPrice = extraPrice.add(productVariantValue3.getPriceExtra());

		}

		if(productVariantValue4 != null)  {

			extraPrice = extraPrice.add(productVariantValue4.getPriceExtra());

		}

		return extraPrice;

	}


	private List<ProductVariant> getProductVariantList(ProductVariantConfig productVariantConfig)  {

		List<ProductVariant> productVariantList = Lists.newArrayList();

		if(productVariantConfig.getProductVariantAttr1() != null && productVariantConfig.getProductVariantValue1Set() != null)  {

			for(ProductVariantValue productVariantValue1 : productVariantConfig.getProductVariantValue1Set())  {

				productVariantList.addAll(this.getProductVariantList(productVariantConfig, productVariantValue1));

			}
		}

		return productVariantList;
	}


	private List<ProductVariant> getProductVariantList(ProductVariantConfig productVariantConfig, ProductVariantValue productVariantValue1)  {

		List<ProductVariant> productVariantList = Lists.newArrayList();

		if(productVariantConfig.getProductVariantAttr2() != null && productVariantConfig.getProductVariantValue2Set() != null)  {

			for(ProductVariantValue productVariantValue2 : productVariantConfig.getProductVariantValue2Set())  {

				productVariantList.addAll(this.getProductVariantList(productVariantConfig, productVariantValue1, productVariantValue2));

			}
		}

		else  {

			productVariantList.add(
					this.createProductVariant(productVariantConfig, productVariantValue1, null, null, 	null));
		}

		return productVariantList;

	}


	private List<ProductVariant> getProductVariantList(ProductVariantConfig productVariantConfig, ProductVariantValue productVariantValue1, ProductVariantValue productVariantValue2)  {

		List<ProductVariant> productVariantList = Lists.newArrayList();

		if(productVariantConfig.getProductVariantAttr3() != null && productVariantConfig.getProductVariantValue3Set() != null)  {

			for(ProductVariantValue productVariantValue3 : productVariantConfig.getProductVariantValue3Set())  {

				productVariantList.addAll(this.getProductVariantList(productVariantConfig, productVariantValue1, productVariantValue2, productVariantValue3));
			}
		}

		else  {

			productVariantList.add(
					this.createProductVariant(productVariantConfig, productVariantValue1, productVariantValue2, null, 	null));
		}

		return productVariantList;

	}


	private List<ProductVariant> getProductVariantList(ProductVariantConfig productVariantConfig, ProductVariantValue productVariantValue1, ProductVariantValue productVariantValue2,
			ProductVariantValue productVariantValue3)  {

		List<ProductVariant> productVariantList = Lists.newArrayList();

		if(productVariantConfig.getProductVariantAttr4() != null && productVariantConfig.getProductVariantValue4Set() != null)  {

			for(ProductVariantValue productVariantValue4 : productVariantConfig.getProductVariantValue4Set())  {

				productVariantList.add(
						this.createProductVariant(productVariantConfig, productVariantValue1, productVariantValue2, productVariantValue3, 	productVariantValue4));
			}
		}

		else  {

			productVariantList.add(
					this.createProductVariant(productVariantConfig, productVariantValue1, productVariantValue2, productVariantValue3, 	null));
		}

		return productVariantList;

	}


	@Override
	public ProductVariant createProductVariant(ProductVariantConfig productVariantConfig, ProductVariantValue productVariantValue1, ProductVariantValue productVariantValue2,
			ProductVariantValue productVariantValue3, ProductVariantValue productVariantValue4)  {

		ProductVariantAttr productVariantAttr1 = null, productVariantAttr2 = null, productVariantAttr3 = null, productVariantAttr4 = null;
		if(productVariantValue1 != null)  {
			productVariantAttr1 = productVariantConfig.getProductVariantAttr1();
		}
		if(productVariantValue2 != null)  {
			productVariantAttr2 = productVariantConfig.getProductVariantAttr2();
		}
		if(productVariantValue3 != null)  {
			productVariantAttr3 = productVariantConfig.getProductVariantAttr3();
		}
		if(productVariantValue4 != null)  {
			productVariantAttr4 = productVariantConfig.getProductVariantAttr4();
		}

		return productVariantService.createProductVariant(
				productVariantAttr1,
				productVariantAttr2,
				productVariantAttr3,
				productVariantAttr4,
				productVariantValue1,
				productVariantValue2,
				productVariantValue3,
				productVariantValue4,
				false);

	}


	@Override
	public Map<String, Object> getDiscountsFromCatalog(SupplierCatalog supplierCatalog,BigDecimal price){
		Map<String, Object> discounts = new HashMap<String, Object>();

		discounts.put("discountAmount", supplierCatalog.getPrice().subtract(price));
		discounts.put("discountTypeSelect", 2);

		return discounts;
	}

}
